using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using SecretLabs.NETMF.Hardware.Netduino;

using mfLogger;

using OST.Shared;
using OST.Shared.Interfaces;
using OST.Shared.Extensions;
using OST.Shared.SharedTypes;
using OST.Persistence;

namespace OST.Motor
{
    /// <summary>
    /// empty <see cref="IMotor"/> implementation
    /// </summary>
    public class MonitorMotor : IMotor
    {
        #region Fields
        OutputPort _swDir = new OutputPort(Pins.GPIO_PIN_D0, false);//true --> MovingUp; false --> MovingDown;
        OutputPort _swDc = new OutputPort(Pins.GPIO_PIN_D1, false);

        InterruptPort _endDown = new InterruptPort(Pins.GPIO_PIN_D8, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeHigh);
        InterruptPort _endUp = new InterruptPort(Pins.GPIO_PIN_D9, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeHigh);

        int _currentPosition = 0;

        #endregion

        #region CTOR
        public MonitorMotor()
        {
            Logger.Warning(GetType().Name + " implementation is experimental and just with basic-control-support");

            _endDown.OnInterrupt += endDown_OnInterrupt;
            _endUp.OnInterrupt -= endUp_OnInterrupt;
        }

        #endregion

        #region Methods

        private void endUp_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Logger.Debug("endUp_OnInterrupt() received");

        }

        private void endDown_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Logger.Debug("endDown_OnInterrupt() received");
        }

        #endregion

        #region Properties

        /// <summary>
        /// port-gathered flag, which is checked, if motor is under current
        /// </summary>
        private bool MotorDriving
        {
            get
            {
                return _swDc.Read();
            }
            set
            {
                _swDc.Write(value);
            }
        }

        /// <summary>
        /// port-gathered flag, which is checked, for: //true --> MovingUp; false --> MovingDown;
        /// </summary>
        private bool MotorDirection
        {
            get
            {
                return _swDir.Read();
            }
            set
            {
                //switch the current, off, if DC is on
                var motorWasDriving = false;
                if (MotorDriving)
                {
                    _swDc.Write(false);
                    Thread.Sleep(100);
                    motorWasDriving = true;
                }

                _swDir.Write(value);
                Thread.Sleep(100);

                //switch the current, on, if DC was on
                if (motorWasDriving)
                {
                    _swDc.Write(true);
                    Thread.Sleep(100);
                }
            }
        }

        /// <summary>
        /// wrapper-property 4 logging
        /// </summary>
        private ILoggerAppender Logger
        {
            get
            {
                return mfLogger.mfLogger.Instance;
            }
        }

        #endregion

        #region IMotor
        public bool IsInitialized { get; private set; }

        public void Initialize()
        {
            InitConfigs();

            IsInitialized = false;

            Logger.Info(GetType().Name + " is initializing");

            MoveUp();
            Thread.Sleep(MotorConfig.Delay + 1000);//ensure, that position is out of end sensor suspension
            MoveDown();
            int timeout = MotorConfig.Duration + 2 * MotorConfig.Delay;
            Thread.Sleep(timeout);

            DrivingDirection = enumMotorDrivingDirection.EndDown;
            _swDc.Write(false);
            IsInitialized = true;

            Logger.Info(this.GetType().Name + " is initialized");

            OnNotifyPropertyChanged("IsInitialized", IsInitialized);

            _currentPosition = 0;
        }

        private void InitConfigs()
        {
            if (!ConfigurationProvider.Instance.HasConfig)
                throw new OSTException("no config found, motor-init");

            MotorConfig = ConfigurationProvider.Instance.ConfigInstance.Motor;
            FrameConfig = ConfigurationProvider.Instance.ConfigInstance.Frame;
        }

        enumMotorDrivingDirection _drivingDirection = enumMotorDrivingDirection.None;
        public enumMotorDrivingDirection DrivingDirection
        {
            get
            {
                return _drivingDirection;
            }
            set
            {
                _drivingDirection = value;
            }
        }

        public int Position
        {
            get
            {
                return _currentPosition;
            }
            private set
            {
                _currentPosition = value;
            }
        }

        public MotorConfiguration MotorConfig { get; set; }

        public FrameConfiguration FrameConfig { get; set; }

        public bool Driving
        {
            get
            {
                throw new NotImplementedException();
            }
        }

        public void MoveDown()
        {
            Logger.Debug("MoveDown() executed");

            MotorDirection = false;
            MotorDriving = true;

            DrivingDirection = enumMotorDrivingDirection.MovingDown;
        }

        public void MoveUp()
        {
            Logger.Debug("MoveUp() executed");

            MotorDirection = true;
            MotorDriving = true;

            DrivingDirection = enumMotorDrivingDirection.MovingUp;
        }

        public void Stop()
        {
            Logger.Debug("Stop() executed");

            MotorDriving = true;
        }

        public void Move2MotorPositionAsync(int motorTarget)
        {
            AssertIfMotorTargetIsInvalid(motorTarget);

            if (MotorDriving)
            {
                Logger.Warning("Move2MotorPositionAsync already invoked");
                return;
            }
            var normedTarget = System.Math.Abs(motorTarget - Position);
            if (normedTarget <= 2)
            {
                Logger.Warning("Move2MotorPositionAsync(motorTarget=" + Position
                    + ") already reached or increment too small " + normedTarget + " mm");
                OnMotorTargetReached(Position, motorTarget, false);
                return;
            }
     
            //set up direction
            if (motorTarget > Position)
            {
                MoveUp();
            }
            else if (motorTarget <= Position)
            {
                MoveDown();
            }
            else
            {
                Logger.Warning("Move2MotorPositionAsync DrivingDirection is not valid");
                return;
            }

            throw new NotImplementedException();
        }

        private void AssertIfMotorTargetIsInvalid(int motorTarget)
        {
            if (motorTarget > FrameConfig.Stroke)
                throw new OSTException("Move2MotorPositionAsync motorTarget=" + motorTarget + " is greater then motor-stroke=" + FrameConfig.Stroke);
            if (motorTarget < 0)
                throw new OSTException("Move2MotorPositionAsync motorTarget have to be positive");
        }

        public event delMotorTargetReached MotorTargetReached;

        private void OnMotorTargetReached(int motorCalculatedPosition, int motorTargetPosition, bool timedOut)
        {
            if (MotorTargetReached != null)
            {
                MotorTargetReached(motorCalculatedPosition, motorTargetPosition, timedOut);
            }
        }

        public void Calibrate()
        { 
            
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// notifies clients over changes in puplic properties
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnNotifyPropertyChanged(String propertyName, object newValue)
        {

            if (PropertyChanged != null && !propertyName.IsNullOrEmpty())
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName, newValue));
            }
        }

        #endregion

    }
}
